package com.wex.app;

import com.wex.app.tools.FileTools;
import com.wex.app.tools.UiActions;
import com.wex.app.tools.UiDisplay;
import com.wex.jni.JniFFmpeg;
import ohos.aafwk.ability.Ability;
import ohos.aafwk.content.Intent;
import ohos.agp.components.*;
import ohos.eventhandler.EventRunner;

import java.io.RandomAccessFile;
import java.text.SimpleDateFormat;
import java.util.Date;

public class CodecTest2Ability extends Ability {

    private TextField textField;
    private Checkbox cbSwitch;
    private Button btnEncAAC;
    private Button btnDecAAC;
    private Button btnEncH264;
    private Button btnDecH264;
    private Button btnEncH264_480p;
    private Button btnDecH264_480p;
    private Button btnExit;

    //文件输入输出
    private RandomAccessFile inGlobal = null;
    private RandomAccessFile outGlobal = null;

    //H26x数据解码前的缓冲区,用于解析和截取帧
    private long ffmpegFd = 0;
    private JniFFmpeg.H26xFiFo h26xFiFo = null;

    //生成log文件
    private RandomAccessFile rafGlobal = null;
    private UiDisplay display = null;
    private boolean detailedDisplay = false;
    private void log(String s)
    {
        //初始化
        if (rafGlobal == null) {
            Date d = new Date();
            SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmss");
            rafGlobal = FileTools.openWriter(getContext(), "ffmpeg-" + sdf.format(d) + ".txt");
        }
        if (display == null)
            display = new UiDisplay(EventRunner.getMainEventRunner(), textField);

        //三路log输出
        System.out.print("----->> " + s); //终端输出
        FileTools.write(rafGlobal, s); //log文件输出
        display.append(s);//ui输出
    }

    //统计(展示在ui窗口)
    private int resultPushBytes = 0;
    private int resultPushFrame = 0;
    private int resultReadBytes = 0;
    private int resultReadFrame = 0;
    private long resultMsBegin = 0;
    private long resultMsEnd = 0;
    private byte[] resultReadSample = new byte[10];
    final private int resultReadFrameLimit = 100;
    private void resultReset() {
        resultPushBytes = resultPushFrame = 0;
        resultReadBytes = resultReadFrame = 0;
        resultMsBegin = resultMsEnd = System.currentTimeMillis();
        for (int i = 0; i < resultReadSample.length; i++)
            resultReadSample[i] = 0;
    }
    private void resultPrint() {
        resultMsEnd = System.currentTimeMillis();
        String info = "\nresult: ****************************************\n"
            + "  pushFrame: " + resultPushFrame + "  pushBytes: " + resultPushBytes + "\n"
            + "  readFrame: " + resultReadFrame + "  readBytes: " + resultReadBytes + "\n"
            + "  readSample(hex): "
            + Integer.toHexString(resultReadSample[0] & 0xFF) + " "
            + Integer.toHexString(resultReadSample[1] & 0xFF) + " "
            + Integer.toHexString(resultReadSample[2] & 0xFF) + " "
            + Integer.toHexString(resultReadSample[3] & 0xFF) + " "
            + Integer.toHexString(resultReadSample[4] & 0xFF) + " "
            + Integer.toHexString(resultReadSample[5] & 0xFF) + " "
            + Integer.toHexString(resultReadSample[6] & 0xFF) + " "
            + Integer.toHexString(resultReadSample[7] & 0xFF) + " "
            + Integer.toHexString(resultReadSample[8] & 0xFF) + " "
            + Integer.toHexString(resultReadSample[9] & 0xFF) + "\n"
            + "  timeBegin: " + resultMsBegin + "ms  timeEnd: " + resultMsEnd + "ms \n"
            + "  timeInterval: " + (resultMsEnd - resultMsBegin) + "ms \n"
            + "************************************************ \n\n";
        log(info);
    }

    //数据塞入编/解码器线程: 运行标志、推数据、线程本体
    private boolean taskRun = false;
    private int pushData()
    {
        byte[] retBytes = null;

        //编码
        if (btnCurrent == btnEncH264 || btnCurrent == btnEncH264_480p) {

            int yuvSize = btnCurrent == btnEncH264 ? (1920 * 1080 * 3 / 2) : (720 * 480 * 3 / 2);

            resultPushFrame += 1;
            resultPushBytes += yuvSize;

            JniFFmpeg.codecPush(ffmpegFd, new byte[yuvSize]);
            retBytes = JniFFmpeg.codecGain(ffmpegFd);
        }
        //解码
        else if (btnCurrent == btnDecH264 || btnCurrent == btnDecH264_480p) {
            byte[] h26xData = null;

            //解码数据准备
            if (inGlobal != null && h26xFiFo != null) {

                if (h26xFiFo.Gaps() > 0) {
                    byte[] readDat = new byte[h26xFiFo.Gaps()];
                    int ret = FileTools.read(inGlobal, readDat);
                    h26xFiFo.Set(readDat, ret);
                }

                h26xData = h26xFiFo.Get();
            }

            if (h26xData != null) {
                //统计
                resultPushFrame += 1;
                resultPushBytes += h26xData.length;

                JniFFmpeg.codecPush(ffmpegFd, h26xData);
                retBytes = JniFFmpeg.codecGain(ffmpegFd);
            }
            else
                return -1;
        }
        else
            return -1;

        //编解码结果
        if (retBytes != null) {

            if (detailedDisplay) {
                if (retBytes.length > 9)
                    log("encode/decode: size/" + retBytes.length + " "
                            + Integer.toHexString(retBytes[0] & 0xFF) + " "
                            + Integer.toHexString(retBytes[1] & 0xFF) + " "
                            + Integer.toHexString(retBytes[2] & 0xFF) + " "
                            + Integer.toHexString(retBytes[3] & 0xFF) + " "
                            + Integer.toHexString(retBytes[4] & 0xFF) + " "
                            + Integer.toHexString(retBytes[5] & 0xFF) + " "
                            + Integer.toHexString(retBytes[6] & 0xFF) + " "
                            + Integer.toHexString(retBytes[7] & 0xFF) + " "
                            + Integer.toHexString(retBytes[8] & 0xFF) + " "
                            + Integer.toHexString(retBytes[9] & 0xFF) + " "
                            + "\n");
                else
                    log("encode/decode: size/" + retBytes.length + "\n");
            }

            //写文件
            if (outGlobal != null)
                FileTools.write(outGlobal, retBytes);

            //统计
            resultReadFrame += 1;
            resultReadBytes += retBytes.length;

            //达到一定帧数后自动关闭
            if (resultReadFrame >= resultReadFrameLimit)
            {
                //采样
                for (int i = 0; i < resultReadSample.length && i < retBytes.length; i++)
                    resultReadSample[i] = retBytes[i];

                //通过UiActions封装,实现在非UI线程调用UI方法btnXXXCallback()
                UiActions act = new UiActions(EventRunner.getMainEventRunner());
                act.action(btnXXXCallbackRun);
            }
        }
        else
            log("encode/decode failed, readFrame/" + resultReadFrame + " readBytes/" + resultReadBytes + "\n");

        return 0;
    }
    private void pushTask()
    {
        log("pushTask run \n");
        while (taskRun)
        {
            if (pushData() != 0) {
                try {
                    Thread.sleep(10);
                } catch (Exception e) {}
            }
        }
        log("pushTask exit \n");

        if (inGlobal != null)
        {
            FileTools.close(inGlobal);
            inGlobal = null;
        }
        if (outGlobal != null)
        {
            FileTools.close(outGlobal);
            outGlobal = null;
        }

        h26xFiFo = null;

        //关闭编解码器
        JniFFmpeg.codecClose(ffmpegFd);
        ffmpegFd = 0;
    }

    //开关线程
    private void startTask()
    {
        Thread thread = new Thread(new Runnable() {
            @Override
            public void run() {
                pushTask();
            }
        });
        taskRun = true;
        resultReset();
        thread.start();
    }
    private void stopTask()
    {
        taskRun = false;
        resultPrint();
    }

    //按键回调
    private Button btnCurrent = null;
    private Runnable btnXXXCallbackRun = new Runnable() {
        @Override
        public void run() {
            //相当于自动点击了一次按键,关闭当前编解码任务
            btnXXXCallback(null);
        }
    };
    private void btnXXXCallback(Button button)
    {
        //关闭当前任务
        if (btnCurrent != null)
        {
            stopTask();
            //按键设置为不透明
            btnCurrent.setAlpha(1.0f);
            btnCurrent = null;
            log("btnXXXCallback stop\n");
        }
        //开始任务
        else if (button != null && ffmpegFd == 0)
        {
            //按键对应编解码初始化以及文件打开准备
            if (button == btnEncAAC) {
                ;
            }
            else if (button == btnDecAAC) {
                ;
            }
            else if (button == btnEncH264) {
                ffmpegFd = JniFFmpeg.codecOpen(JniFFmpeg.Type_Encode, JniFFmpeg.Format_H264, 1920, 1080, 30);
            }
            else if (button == btnDecH264) {
                inGlobal = FileTools.openReader(getContext(), "1080P.h264");
                if (inGlobal == null) {
                    log("未找到文件: /Android/media/com.wex.app/1080P.h264 \n");
                }
                else {
                    ffmpegFd = JniFFmpeg.codecOpen(JniFFmpeg.Type_Decode, JniFFmpeg.Format_H264, 1920, 1080, 30);
                    if (ffmpegFd > 0)
                        h26xFiFo = new JniFFmpeg.H26xFiFo(1024 * 800, 3);
                }
            }
            else if (button == btnEncH264_480p) {
                ffmpegFd = JniFFmpeg.codecOpen(JniFFmpeg.Type_Encode, JniFFmpeg.Format_H264, 720, 480, 30);
            }
            else if (button == btnDecH264_480p) {
                inGlobal = FileTools.openReader(getContext(), "480P.h264");
                if (inGlobal == null) {
                    log("未找到文件: /Android/media/com.wex.app/480P.h264 \n");
                }
                else {
                    ffmpegFd = JniFFmpeg.codecOpen(JniFFmpeg.Type_Decode, JniFFmpeg.Format_H264, 720, 480, 30);
                    if (ffmpegFd > 0)
                        h26xFiFo = new JniFFmpeg.H26xFiFo(1024 * 500, 3);
                }
            }

            //开始编解码
            if (ffmpegFd > 0)
            {
                startTask();
                //按键设置为半透明
                btnCurrent = button;
                btnCurrent.setAlpha(0.5f);
                log("btnXXXCallback start\n");
            }
        }
    }
    private void btnEncAACCallback(Component component)
    {
        log("btnEncAACCallback\n");
        btnXXXCallback(btnEncAAC);
    }
    private void btnDecAACCallback(Component component)
    {
        log("btnDecAACCallback\n");
        btnXXXCallback(btnDecAAC);
    }
    private void btnEncH264Callback(Component component)
    {
        log("btnEncH264Callback\n");
        btnXXXCallback(btnEncH264);
    }
    private void btnDecH264Callback(Component component)
    {
        log("btnDecH264Callback\n");
        btnXXXCallback(btnDecH264);
    }
    private void btnEncH264_480pCallback(Component component)
    {
        log("btnEncH264_480pCallback\n");
        btnXXXCallback(btnEncH264_480p);
    }
    private void btnDecH264_480pCallback(Component component)
    {
        log("btnDecH264_480pCallback\n");
        btnXXXCallback(btnDecH264_480p);
    }
    private void btnExitCallback(Component component)
    {
        log("btnExitCallback\n");
        terminateAbility();
    }

    //获取ui控件控制符
    private void initComponents() {
        textField = (TextField) findComponentById(ResourceTable.Id_codec_test_text);
        cbSwitch = (Checkbox) findComponentById(ResourceTable.Id_codec_cb_switch);
        btnEncAAC = (Button) findComponentById(ResourceTable.Id_codec_test_btn_enc_aac);
        btnDecAAC = (Button) findComponentById(ResourceTable.Id_codec_test_btn_dec_aac);
        btnEncH264 = (Button) findComponentById(ResourceTable.Id_codec_test_btn_enc_h264);
        btnDecH264 = (Button) findComponentById(ResourceTable.Id_codec_test_btn_dec_h264);
        btnEncH264_480p = (Button) findComponentById(ResourceTable.Id_codec_test_btn_enc_h264_480p);
        btnDecH264_480p = (Button) findComponentById(ResourceTable.Id_codec_test_btn_dec_h264_480p);
        btnExit = (Button) findComponentById(ResourceTable.Id_codec_test_btn_exit);

        btnEncAAC.setEnabled(false);
        btnEncAAC.setAlpha(0.5f);
        btnDecAAC.setEnabled(false);
        btnDecAAC.setAlpha(0.5f);

        btnEncAAC.setClickedListener(this::btnEncAACCallback);
        btnDecAAC.setClickedListener(this::btnDecAACCallback);
        btnEncH264.setClickedListener(this::btnEncH264Callback);
        btnDecH264.setClickedListener(this::btnDecH264Callback);
        btnEncH264_480p.setClickedListener(this::btnEncH264_480pCallback);
        btnDecH264_480p.setClickedListener(this::btnDecH264_480pCallback);
        btnExit.setClickedListener(this::btnExitCallback);
        cbSwitch.setCheckedStateChangedListener(new AbsButton.CheckedStateChangedListener() {
            @Override
            public void onCheckedChanged(AbsButton absButton, boolean b) {
                detailedDisplay = b;
            }
        });
    }

    @Override
    public void onStart(Intent intent) {
        super.onStart(intent);
        super.setUIContent(ResourceTable.Layout_codec_test_ability);
        //获取ui控件控制符
        initComponents();
    }

    @Override
    public void onStop() {
        super.onStop();
        stopTask();
        FileTools.close(rafGlobal);
    }
}
